package jmpo.eh;

import java.util.Optional;

/**
 * В некоторых ситуациях функция не может вернуть разумный результат для любых
 * комбинаций входных данных. Например, рассмотрим класс Segment,
 * который определяет отрезок на целочисленной прямой.
 */
/**
 * Represents a segment of the integer numbers specified by the most left (begin) 
 * and the most right (end) integer points on a number line.
 * The both ends are included. 
 */
final class Segment {
    /**
     * @param begin the most left point of the segment, minimal value
     * @param end   the most right point of the segment, maximum value
     * @throws IllegalArgumentException if (begin > end)
     */
    Segment(long begin, long end) {
        if (begin > end) 
            throw new IllegalArgumentException("The segment with end before begin");
        this.begin = begin;
        this.end = end;
    }

    /**
     * Checks if tne segment contains the given number
     * @param number a number to check
     * @return a boolean: true - contains, false - does not contain
     */
    boolean contains(long number) {
        return begin <= number && end >= number;
    }

    /**
     * Calculates and constructs a new Segment that is an intersection of 
     * a given Segment with this.
     * @param seg a segment to intersect with
     * @return a new Segment
     * @throws IllegalArgumentException in case segments do not intersect
     */
    Segment intersect(Segment set) {
        return new Segment(Math.max(this.begin, set.begin), Math.min(this.end, set.end));
    }
    private long begin, end;
}

/**
 * В приведённом выше классе метод intersect отрабатывает успешно, если 
 * пересечение отрезков действительно существует, но если его нет, 
 * то возникнет исключение в конструкторе отрезка, так как его конец будет 
 * раньше его начала. К сожалению, мы не можем вернуть никакого разумного 
 * значения Segment, если отрезки не пересекаются. Нам необходимо сигнализирвоать
 * клиенсткому коду, что найти пересечение не удалось.
 * Если такая ошибка не будет часто возникать в коде, то исключение - это
 * допустимый выход, но если это будет вполне нормальным и нам просто нужно 
 * выбрать иной путь или сконструировать некий "отрезок по умолчанию", то
 * исключения сильно засорят наш код обработкой.
 * Начиная с Java 8 было введена возможность использовать специальный 
 * объект-обёртку Optional, который позволяет сконструировать объект 
 * со значением, либо сконструировать пустую обёртку.
 * Напишем новый класс ModernSegment, который использует эту возможность.
 */
final class ModernSegment {
    ModernSegment(long begin, long end) {
        if (begin > end) 
            throw new IllegalArgumentException("The segment with end before begin");
        this.begin = begin;
        this.end = end;
    }

    boolean contains(long number) {
        return begin <= number && end >= number;
    }

    /**
     * Calculates and constructs a new Segment that is an intersection of 
     * a given ModernSegment with this.
     * @param seg a ModernSegment to intersect with
     * @return an empty Optional if there's no intersection and a good Optional otherwise
     */
    Optional<ModernSegment> intersect(ModernSegment set) {
        var newBegin = Math.max(this.begin, set.begin);
        var newEnd   = Math.min(this.end, set.end);
        if (newBegin > newEnd) return Optional.empty();
        return Optional.of(new ModernSegment(newBegin, newEnd));
    }
    private long begin, end;
}

public class OptionalExample {
    private static boolean optionalTest() {
        var s1 = new ModernSegment(10, 100);
        var s2 = new ModernSegment(-10, 20);
        var s3 = new ModernSegment(25, 150);
        var op1 = s1.intersect(s2);
        var op2 = s2.intersect(s3);
        return op1.isPresent() && !op2.isPresent();
    }

    private static void test(boolean expectTrue) {
        if (expectTrue) {
            System.out.println("Ok!");
            return;
        }
        throw new RuntimeException();
    }

    public static void main(String[] args) {
        test(optionalTest());
    } 
}
